import { KeymapEventHandler, MarkdownView, Plugin, setIcon } from "obsidian";
import Heading from "./heading";
import { SharedSuggestModal } from "./sharedTypes";
import HeadingTree from "./headingTree";
import { MathUtil } from "myTypeScriptLib/scripts/utils/mathUtil";

export default class SearchOutlineModal extends SharedSuggestModal<Heading> {

    private _plugin: Plugin = null;
    private _headingTree: HeadingTree = null;

    /**如果搜索框有内容，表示有在搜索 */
    private _isSearching: boolean = false;

    constructor(plugin: Plugin, tree: HeadingTree) {
        super(plugin.app);
        this._plugin = plugin;
        this._headingTree = tree;

        this._registerKeyEventListener();
    }

    onOpen() {
        super.onOpen();
        this._setSelectedItem(this._getCurrentLocation());
    }

    private _registerKeyEventListener() {
        let scope = this._getSharedScope();

        // 注销一些按键事件
        let keys = ['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown']
        let handlers: KeymapEventHandler[] = [];
        scope.keys.forEach(v => {
            if (keys.contains(v.key)) {
                handlers.push(v);
            }
        });
        handlers.forEach(v => { scope.unregister(v); });

        scope.register(['Ctrl'], 'Enter', this._backOutlineMode.bind(this));
        scope.register(['Ctrl'], 'k', this._moveUp.bind(this));
        scope.register([], 'ArrowUp', this._moveUp.bind(this));
        scope.register(['Ctrl'], 'j', this._moveDown.bind(this));
        scope.register([], 'ArrowDown', this._moveDown.bind(this));
        scope.register(['Ctrl'], 'u', this._halfPageUp.bind(this));
        scope.register(['Ctrl'], 'd', this._halfPageDown.bind(this));
        scope.register(['Ctrl'], 'b', this._pageUp.bind(this));
        scope.register(['Ctrl'], 'f', this._pageDown.bind(this));
        scope.register(['Ctrl'], 'i', this._moveToStart.bind(this));
        scope.register(['Ctrl'], 'n', this._moveToEnd.bind(this));
        scope.register(['Ctrl'], 'h', this._jumpToParentHeading.bind(this));
        scope.register([], 'ArrowLeft', this._jumpToParentHeading.bind(this));
        scope.register(['Ctrl', 'Shift'], 'h', this._jumpToParentHeadingAndFold.bind(this));
        scope.register(['Ctrl'], 'l', this._toggleFold.bind(this));
        scope.register([], 'ArrowRight', this._toggleFold.bind(this));
        scope.register(['Ctrl', 'Shift'], 'l', this._toggleBrotherFold.bind(this));

        for (let i = 0; i < 4; i++) {
            scope.register(['Ctrl', 'Alt'], i.toString(), this._setFoldLevel.bind(this));
        }
    }

    getSuggestions(query: string): Heading[] | Promise<Heading[]> {
        query = query.trim();
        if (query.length == 0) {
            this._isSearching = false;
            return this._headingTree.visibleHeadings;
        }
        else {
            this._isSearching = true;
            return this._headingTree.search(query);
        }
    }
    renderSuggestion(value: Heading, el: HTMLElement) {
        if (this._isSearching) el.createSpan({ text: value.name });
        else this._renderSuggestionInOutlineMode(value, el);
    }
    onChooseSuggestion(item: Heading, evt: MouseEvent | KeyboardEvent) {
        let line = item.line;
        let view = this._plugin.app.workspace.getActiveViewOfType(MarkdownView);

        // Markdown 跳转到该标题处
        if (view) {
            view.setEphemeralState({ line });
        }
    }

    private _moveDown() {
        if (this._emptySuggestion()) return;

        this._moveSelection(1);
        this._keepSelectionCentered();
    }
    private _moveUp() {
        if (this._emptySuggestion()) return;

        this._moveSelection(-1);
        this._keepSelectionCentered();
    }
    private _useSelectedItem(evt: KeyboardEvent) {
        if (this._emptySuggestion()) return;

        this._getChooser().useSelectedItem(evt);
    }
    private _halfPageDown() {
        if (this._emptySuggestion()) return;

        this._moveSelectionWithoutLoop(Math.floor(this._maxShowedItemSum() / 2));
    }
    private _halfPageUp() {
        if (this._emptySuggestion()) return;

        this._moveSelectionWithoutLoop(-Math.floor(this._maxShowedItemSum() / 2));
    }
    private _pageDown() {
        if (this._emptySuggestion()) return;

        this._moveSelectionWithoutLoop(this._maxShowedItemSum());
    }
    private _pageUp() {
        if (this._emptySuggestion()) return;

        this._moveSelectionWithoutLoop(-this._maxShowedItemSum());
    }
    private _moveToStart() {
        if (this._emptySuggestion()) return;

        this._setSelectedItem(0);
    }
    private _moveToEnd() {
        if (this._emptySuggestion()) return;

        this._setSelectedItem(this._getChooser().values.length - 1);
    }
    private _backOutlineMode() {
        if (this._emptySuggestion() || !this._isSearching) return;

        let chooser = this._getChooser();
        let index = chooser.selectedItem;
        let selectedHeading = chooser.values.at(index);

        this.inputEl.value = '';
        this._headingTree.setAllExpand(true);

        this._headingTree.reset();
        this._updateSuggestions();

        this._setSelectedItem(selectedHeading);
    }
    private _setFoldLevel(evt: KeyboardEvent) {
        if (this._emptySuggestion() || this._isSearching) return;

        let level = Number.parseInt(evt.key);
        this._headingTree.setFoldLevel(level);

        // 选择位置 沿父子链向上移动至级别为level的位置
        let selectedHeading = this._getSelectedHeading();
        if (selectedHeading.level > level + 1) selectedHeading = selectedHeading.findParentByLevel(level + 1);

        this._headingTree.reset();
        this._updateSuggestions();

        this._setSelectedItem(selectedHeading);
    }
    /**跳到父标题 */
    private _jumpToParentHeading() {
        if (this._emptySuggestion() || this._isSearching) return;

        let chooser = this._getChooser();
        let index = chooser.selectedItem;
        let selectedHeading = chooser.values.at(index);

        if (selectedHeading.haveParent()) {
            this._setSelectedItem(selectedHeading.parent);
        }
    }
    /**跳到父标题，并折叠父标题及子标题 */
    private _jumpToParentHeadingAndFold() {
        if (this._emptySuggestion() || this._isSearching) return;

        let chooser = this._getChooser();
        let index = chooser.selectedItem;
        let selectedHeading = chooser.values.at(index);
        if (!selectedHeading.haveParent()) return;

        index = chooser.values.indexOf(selectedHeading.parent);
        selectedHeading.parent.toggleFold();

        this._headingTree.reset();
        this._updateSuggestions();
        this._setSelectedItem(index);
    }
    /**切换标题的折叠或展开 */
    private _toggleFold() {
        if (this._emptySuggestion() || this._isSearching) return;

        let chooser = this._getChooser();
        let index = chooser.selectedItem;
        let selectedHeading = chooser.values.at(index);

        if (selectedHeading.isLeaf()) return;

        selectedHeading.toggleFold();
        this._headingTree.reset();
        this._updateSuggestions();
        this._setSelectedItem(index);
    }
    /**折叠或展开自己和所有兄弟标题；如果活动标题没有子标题，那么折叠兄弟标题 */
    private _toggleBrotherFold() {
        if (this._emptySuggestion() || this._isSearching) return;

        let chooser = this._getChooser();
        let index = chooser.selectedItem;
        let selectedHeading = chooser.values.at(index);

        let toExpand = !selectedHeading.isExpand;
        if (selectedHeading.isLeaf()) {
            toExpand = false;
        }

        selectedHeading.parent.children.forEach(v => v.isExpand = toExpand);

        this._headingTree.reset();
        this._updateSuggestions();

        this._setSelectedItem(selectedHeading);
    }

    /**移动选择项 */
    private _moveSelection(dir: 1 | -1) {
        let index = MathUtil.loopLimit(this._getChooser().selectedItem + dir, this.resultContainerEl.children.length);
        this._setSelectedItem(index);
    }

    private _moveSelectionWithoutLoop(delta: number) {
        let chooser = this._getChooser();
        let count = chooser.suggestions.length;
        let index = chooser.selectedItem;

        index += delta;
        if (index < 0) index = 0;
        else if (index >= count) index = count - 1;

        this._setSelectedItem(index);
    }

    private _setSelectedItem(index: number): void;
    private _setSelectedItem(heading: Heading): void;
    private _setSelectedItem(arg: number | Heading) {
        if (arg === null) return;

        let chooser = this._getChooser();
        let index = -1;

        if (arg instanceof Heading) index = chooser.values.findIndex(v => v.equal(arg as Heading));
        else index = arg as number;

        if (index < 0) return;

        chooser.setSelectedItem(index, null);
        chooser.suggestions[chooser.selectedItem].scrollIntoView({ behavior: "auto", block: "center" });
    }

    /**保持选择项在可视区域居中显示 */
    private _keepSelectionCentered() {
        let chooser = this._getChooser();
        chooser.suggestions[chooser.selectedItem].scrollIntoView({ behavior: "auto", block: "center" });
    }

    private _emptySuggestion(): boolean {
        return this.resultContainerEl.children.length == 0;
    }

    private _maxShowedItemSum(): number {
        if (this._emptySuggestion()) return 8;

        let containerHeight = this.resultContainerEl.clientHeight;
        let itemHeight = this.resultContainerEl.children.item(0).clientHeight;
        return Math.floor(containerHeight / itemHeight);
    }

    /**获得 Markdown文档当前位置所在的标题 */
    private _getCurrentLocation(): Heading {
        let view = this._plugin.app.workspace.getActiveViewOfType(MarkdownView);
        let line = Math.floor(view.currentMode.getScroll() + 8);
        return this._headingTree.locateHeading(line);
    }

    /**在非搜索状态下 */
    private _renderSuggestionInOutlineMode(value: Heading, el: HTMLElement) {
        let div = el.createDiv();

        const indent = 15;
        const iconSize = 20;

        let style = `padding-left:${indent * (value.level - 1)}px`;
        div.setAttribute('style', style);
        if (value.isLeaf()) {
            // 缩进
            let style = `padding-left:${indent * (value.level - 1) + iconSize}px`;
            div.setAttribute('style', style);
        }
        else {
            // 缩进
            let style = `padding-left:${indent * (value.level - 1)}px`;
            div.setAttribute('style', style);

            // 扩展或折叠图标 https://lucide.dev/
            setIcon(div, value.isExpand ? 'chevron-down' : 'chevron-right');
            let elements = div.getElementsByClassName('svg-icon');
            if (elements.length > 0) elements.item(0).setAttrs({ width: iconSize, height: iconSize });
        }

        div.createSpan({ text: value.name });
    }

    private _getSelectedHeading(): Heading {
        let chooser = this._getChooser();
        return chooser.values.at(chooser.selectedItem);
    }

}
